package com.octopus.registry;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.octopus.json.Json;
import com.octopus.json.JsonUtils;
import com.octopus.registry.annotation.Service;
import com.octopus.string.StringUtils;
import com.octopus.tree.TreeBranch;
import com.octopus.tree.TreeLeaf;
import com.octopus.tree.Unit;
import com.octopus.tree.imp.simple.AlreadyInTreeStructureException;
import com.octopus.tree.imp.simple.NameAlreadyExistException;
import com.octopus.tree.imp.simple.TreeImp;
import com.octopus.tree.imp.simple.TreeLeafImp;

public class ServiceRegistry {
	
	private static final Logger logger = Logger.getLogger(ServiceRegistry.class.getName());
	
	public static final String ROOT_SIGN = "Registry";
	
	private TreeImp innerTree;
	
	public ServiceRegistry(){
		 defaultInit();
	}
	
	public void defaultInit(){
		innerTree = new TreeImp(ROOT_SIGN);
	}
	
	public ServiceRegistry(String postfix){
		StringUtils.StringContainer sc = new StringUtils.StringContainer(postfix);
		if(StringUtils.isNullString(sc)){
			defaultInit();
		}else{
			innerTree = new TreeImp(StringUtils.concat(ROOT_SIGN, "_", sc.getAfterTrimedString()));
		}
	}
	
	public synchronized void register(String className)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		Class klass = Class.forName(className);
		register(klass);
	}
	
	public synchronized void register(Class<?> klass) throws InstantiationException, IllegalAccessException{
		Service classRootAnnotation = klass.getAnnotation(Service.class);
		String rootNamespace = classRootAnnotation != null ? classRootAnnotation.namespace() : null;
		if(rootNamespace == null){
			rootNamespace = klass.getName();
		}
		Method[] methods = klass.getMethods();
		Object serviceObject = null;
		for(Method method : methods){
			ServiceUnit serviceUnit = null;
			Class<?>[] parameters = method.getParameterTypes();
			if(parameters == null || parameters.length != 1){
				continue;
			}
			if(!parameters[0].getName().equals(Json.class.getName())){
				continue;
			}
			Class<?> returnType = method.getReturnType();
			
			if(!returnType.equals(Void.TYPE)){
				continue;
			}
			Service methodServiceAnnotation = method.getAnnotation(Service.class);
			String methodServiceNamespace = null;
			String methodServiceServiceName = null;
			if(methodServiceAnnotation != null){
				if(methodServiceAnnotation.notRegisterAsService()){
					continue;
				}
				methodServiceNamespace = (methodServiceAnnotation.namespace() == null ? rootNamespace : methodServiceAnnotation.namespace());
				methodServiceServiceName = (methodServiceAnnotation.servicename() == null ? method.getName() : methodServiceAnnotation.servicename());
			}else{
				methodServiceNamespace = rootNamespace;
				methodServiceServiceName = method.getName();
			}
			
			TreeLeaf alreadyNode = innerTree.getDefaultTreeLeaf(methodServiceNamespace, methodServiceServiceName);
			if(alreadyNode != null){
				logger.error(StringUtils.concat("Service name conflict Namespace[",methodServiceNamespace,"]Name[",methodServiceServiceName,"] Please check the service @Annotation"));
				continue;
			}
			
			int modifier = method.getModifiers();
			boolean isStatic = Modifier.isStatic(modifier);
			boolean isPublic = Modifier.isPublic(modifier);
			if(isPublic){
				if(!isStatic){
					if(serviceObject == null){
						try {
							serviceObject = klass.newInstance();
						} catch (InstantiationException e) {
							logger.error(StringUtils.concat("When create new instance of class[",klass.getName(),"], an InstantiationException was thrown out."));
							throw e;
						} catch (IllegalAccessException e) {
							logger.error(StringUtils.concat("When create new instance of class[",klass.getName(),"], an IllegalAccessException was thrown out."));
							throw e;
						}
					}
					serviceUnit = new ServiceUnit(klass, serviceObject, false, method);
				}else{
					serviceUnit = new ServiceUnit(klass, null, true, method);
				}
			}else{
				logger.info(StringUtils.concat("The method [",methodServiceServiceName,"] of the the class[",klass.getName(),"] is not public status[UNREGISTER]"));
			}
			
			
			TreeBranch tb = innerTree.createSimpleSubTreeBranch(methodServiceNamespace);
			if(tb != null){
				TreeLeafImp tli = new TreeLeafImp(methodServiceServiceName, null , null, null);
				tli.setObjectContent(serviceUnit);
				try {
					tb.addTreeLeaf(tli);
					logger.info(StringUtils.concat("The Service [",methodServiceServiceName,"] of the the Namespace [",methodServiceNamespace,"] status[REGISTER]"));
				} catch (AlreadyInTreeStructureException e) {
					//Never go here
					logger.error(e);
					continue;
				} catch (NameAlreadyExistException e) {
					logger.error(StringUtils.concat("Please check the configuraion or service implementation class, because of the name duplicated["+tli.getName()+"] under ["+methodServiceNamespace+"][",serviceUnit.toString(),"]"));
					continue;
				}
			}else{
				logger.error(StringUtils.concat("Unable create tree structure [",methodServiceNamespace,"]"));
				return;
			}
		}
	}
	
	public static void invokeService(ServiceUnit su, Json parameter) throws ServiceInvokeNullParameterException, ServiceDoesNotExistException, MethodIsNullException{
		if(su == null){
			throw new ServiceDoesNotExistException();
		}
		if(parameter == null){
			throw new ServiceInvokeNullParameterException();
		}
		Call call = new Call();
		parameter.putReservedMap(ServiceConstants.SYSTEM_RESERVED_SIMPLE_CALL, call);
		if(su.isStatic()){
			if(su.getMethod() != null){
				try {
					su.getMethod().invoke(null, parameter);
					call.setStatus(CallStatus.success);
				} catch (IllegalArgumentException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_IllegalArgumentException(e);
				} catch (IllegalAccessException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_IllegalAccessException(e);
				} catch (InvocationTargetException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_InvocationTargetException(e);
				} catch (NullPointerException e){
					call.setStatus(CallStatus.fail);
					call.setSystemException_NullPointerException(e);
				} catch (ExceptionInInitializerError e){
					call.setStatus(CallStatus.fail);
					call.setSystemException_ExceptionInInitializerError(e);
				}
			}else{
				throw new MethodIsNullException();
			}
		}else{
			if(su.getMethod() != null){
				try {
					su.getMethod().invoke(su.getObject(), parameter);
					call.setStatus(CallStatus.success);
				} catch (IllegalArgumentException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_IllegalArgumentException(e);
				} catch (IllegalAccessException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_IllegalAccessException(e);
				} catch (InvocationTargetException e) {
					call.setStatus(CallStatus.fail);
					call.setSystemException_InvocationTargetException(e);
				} catch (NullPointerException e){
					call.setStatus(CallStatus.fail);
					call.setSystemException_NullPointerException(e);
				} catch (ExceptionInInitializerError e){
					call.setStatus(CallStatus.fail);
					call.setSystemException_ExceptionInInitializerError(e);
				}
			}else{
				throw new MethodIsNullException();
			}
		}
	}
	
	public void invokeService(String namespace, String serviceName, Json parameter) throws ServiceInvokeNullParameterException, ServiceDoesNotExistException, MethodIsNullException{
		if(parameter == null){
			throw new ServiceInvokeNullParameterException();
		}
		StringUtils.StringContainer sc = new StringUtils.StringContainer(namespace);
		StringUtils.StringContainer sc2 = new StringUtils.StringContainer(serviceName);
		if(StringUtils.isNullString(sc)){
			return;
		}
		if(StringUtils.isNullString(sc2)){
			return;
		}
		TreeLeaf treeLeaf = this.innerTree.getDefaultTreeLeaf(namespace, serviceName);
		if(treeLeaf == null){
			throw new ServiceDoesNotExistException(StringUtils.concat("Namespace[",namespace,"]ServiceName[",serviceName,"]"));
		}
		Object serviceObject = treeLeaf.getObjectContent();
		if(serviceObject != null && serviceObject instanceof ServiceUnit){
			ServiceUnit su = (ServiceUnit)serviceObject;
			invokeService(su, parameter);
		}
	}
	
	public Json invokeServiceSimple(String namespace, String serviceName, Json parameter){
		try {
			invokeService(namespace, serviceName, parameter);
			return null;
		} catch (ServiceInvokeNullParameterException e) {
			Call call = new Call();
			call.setStatus(CallStatus.fail);
			call.setPreException_serviceInvokeNullParameterException(e);
			Json json = JsonUtils.createJsonObject();
			json.putReservedMap(ServiceConstants.SYSTEM_RESERVED_SIMPLE_CALL, json);
			return json;
		} catch (ServiceDoesNotExistException e) {
			Call call = new Call();
			call.setStatus(CallStatus.fail);
			call.setPreException_serviceDoesNotExistException(e);
			return null;
		} catch (MethodIsNullException e) {
			Call call = new Call();
			call.setStatus(CallStatus.fail);
			call.setPreException_methodIsNullException(e);
			return null;
		}
	}
	
	public void invokeService(List<Unit> rules, Json parameter) throws ServiceInvokeNullParameterException, ServiceDoesNotExistException, MethodIsNullException{		
		if(parameter == null){
			throw new ServiceInvokeNullParameterException();
		}
		if(rules == null || rules.isEmpty()){
			throw new ServiceDoesNotExistException();
		}
		if(innerTree != null){
			List<TreeLeaf> leafList = TreeImp.findTreeLeaf(rules, innerTree.getRoot());
			if(leafList != null && !leafList.isEmpty()){
				TreeLeaf treeLeaf = leafList.get(0);
				Object object = treeLeaf.getObjectContent();
				if(object != null && object instanceof ServiceUnit){
					ServiceUnit su = (ServiceUnit)object;
					invokeService(su, parameter);
				}
			}
		}
	}
	
	public String toServiceXMLString(){
		return this.innerTree.toPermanent();
	}
	
	public static void main(String args[]){
		System.out.println(ServiceRegistry.class.getName());
	}
	
}
